Skip to main content

Hooks useLayoutEffect

概念说明

与 useEffect 区别

  • useLayoutEffect 会阻塞浏览器渲染真实 DOM,优先执行 Effect 链表中的 callback;

  • useEffect 不会阻塞浏览器渲染真实 DOM,在渲染真实 DOM 的同时,去执行 Effect 链表中的 callback;

  • useLayoutEffect 设置的 callback 要优先于 useEffect 去执行! !

  • 在两者设置的 callback 中,依然可以获取 DOM 元素原因: 真实 DOM 对象已经创建了,区别只是浏览器是否渲染

  • 如果在 callback 函数中又修改了状态值 视图又要更新

    • useEffect:浏览器肯定是把第一次的真实已经绘制了,再去渲染第二次真实 DOM
    • useLayoutEffect:浏览器是把两次真实 DOM 的渲染,合并在一起渲染的

视图更新的步骤

  • 第一步: 基于 babel-preset-react-app 把 JSX 编译为 createElement 格式
  • 第二步: 把 createElement 执行,创建出 virtualDOM
  • 第三步: 基于 root.render 方法把 virtualDOM 变为真实 DOM 对象 DOM-DIFF
    • useLayoutEffect 阻塞第四步操作,先去执行 Effect 链表中的方法同步操作
    • useEffect 第四步操作和 Effect 链表中的方法执行,是同时进行的异步操作
  • 第四步: 浏览器渲染和绘制真实 DOM 对象
import React, { useEffect, useLayoutEffect, useState } from "react"

export default function Demo() {
let [val, setVal] = useState(0)

useEffect(() => {
console.log("useEffect后执行")
console.log("与dom渲染同时执行的")
console.log("相对于浏览器dom渲染是异步操作")
}, [val])

useLayoutEffect(() => {
console.log("useLayoutEffect先执行")
console.log("在dom元素渲染完成前就会执行完毕")
console.log("相对于浏览器渲染dom是同步操作")
}, [val])

const handler = () => {
setVal(val + 1)
}
return (
<>
<div>{val}</div>
<button onClick={handler}>hello</button>
</>
)
}
tip
  • React 的 useEffect 和 useLayoutEffect 都是 React 中的副作用钩子,但它们的主要区别在于触发时机。

    • useEffect 会在渲染完成后异步执行,不会阻塞浏览器的绘制操作。它适用于需要在组件渲染后执行副作用的情况,例如数据的获取、订阅事件等。然而,它不会阻止屏幕更新,因此可能会导致渲染的一次跳跃,用户可能会在页面渲染完成后才看到最终效果。

    • 而 useLayoutEffect 的触发时机稍早于 useEffect,在浏览器执行绘制之前同步执行。它适用于需要在 DOM 更新之后同步执行副作用的情况,例如获取 DOM 元素的尺寸、位置等。由于它会在页面渲染之前执行,因此可以阻止屏幕更新,确保副作用的执行不会引起渲染跳跃,提供更流畅的用户体验。

    • 需要注意的是,由于 useLayoutEffect 会在 DOM 操作之后同步执行,如果执行的操作非常耗时,则可能导致页面响应变慢。在大多数情况下,使用 useEffect 即可满足需求,只有在确实需要在 DOM 更新后立即执行副作用时才考虑使用 useLayoutEffect。